﻿
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

namespace Contacts
{
	public class ContactScarfingBizRules : Syrinx.BaseDataScarfBizRules
	{
		public void spiderScarfContact(IApplication app, IRuleContext context, IBizObj scarfInfo, string contactId, int maxDepth, int currentDepth)
		{
			DateTime startTime = DateTime.Now;
			bool setStart = false;
			if (setStart = (DateTime.MinValue.CompareTo(scarfInfo.get("runtime.startTime")) == 0))
				scarfInfo.put("runtime.startTime", startTime);


			IMap uniqueContacts = scarfInfo.get("runtime.uniqueContacts") as IMap;

			if(uniqueContacts.containsField(contactId))
				return;
			FlexiMap info = new FlexiMap();
			info.put("extId", contactId);
			IBizObj contact = app.appData.get("DA.Contacts.GetContactByExtId", info) as IBizObj;

			//if (existContact != null)
			//{
			//    uniqueContacts.put(contactId, existContact.get("fullName"));
			//    return;
			//}

			string url = (string)scarfInfo.get("contactDetailsPageUrl");
			url = url.Replace("$extId$", contactId);
			string contactPage = getPageText(url, scarfInfo);

			if (contact == null)
			{
				Regex contactRe = new Regex((string)scarfInfo.get("baseContactDetailRegex"), RegexOptions.Singleline);
				Match m = contactRe.Match(contactPage);
				if (m.Success)
				{
					string fullName = m.Groups["fullName"].Value;
					string birthDate = m.Groups["birthDate"].Value;
					string imgId = m.Groups["imgId"].Value;
					uniqueContacts.put(contactId, fullName);

					//TODO: This if check is specific to imdb, move to config somehow
					bool hasImg = !Cmn.IsEmpty(imgId);
					if (!hasImg && !setStart) //We only want to spider actors that are popular enough to have an img on imdb
					{
						//TODO: Trace it.
						//return;
					}
					lock (scarfInfo)
					{
						uniqueContacts.put(contactId, fullName);
					}
					if (Cmn.IsEmpty(birthDate))
					{
						string day = m.Groups["birthDateDay"].Value;
						string mon = m.Groups["birthDateMon"].Value;
						string year = m.Groups["birthDateYear"].Value;
						if (!Cmn.IsEmpty(day) && !Cmn.IsEmpty(mon) && !Cmn.IsEmpty(year))
							birthDate = string.Format("{0} {1}, {2}", mon, day, year);
					}

					contact = app.bizObjMgr.create("Contact");
					contact.put("id", new Swaf.BGuid.Guid());
					contact.put("extId", contactId);
					string[] parts = fullName.Split(' ');
					if (parts.Length >= 1)
						contact.put("firstName", parts[0]);
					if (parts.Length == 2)
						contact.put("lastName", parts[1]);
					else if (parts.Length > 2)
						for (int p = 1; p < parts.Length; p++)
							contact.put("lastName", contact.get("lastName") + " " + parts[p] + ((p == parts.Length + 1) ? "" : " "));
					contact.put("type.id", scarfInfo.get("contactType"));
					app.ruleMgr.execute("updateContact", contact, false);
					lock (scarfInfo)
					{
						scarfInfo.put("runtime.totalNewContactsFound", ((int)scarfInfo.get("runtime.totalNewContactsFound")) + 1);
					}
				}
				else
					writeTrace(app, scarfInfo, "<ContactScarf id='{0}' event='contactScarfPageSearchFailure' url='{1}' contactId='{2}' />",
						scarfInfo.get("id"), url, contactId);
			}

			if (contactPage != null && currentDepth < maxDepth)
			{
				IBizObjList connectors = scarfInfo.get("connectors") as IBizObjList;
				IRuleGroup connectorScarf = null;
				if (connectors != null)
					foreach (IBizObj connector in connectors)
					{
						Regex sectionRe = new Regex((string)connector.get("sectionRegex"), RegexOptions.Singleline);
						Regex conRe = new Regex((string)connector.get("elementRegex"), RegexOptions.Singleline);
						Match m2 = sectionRe.Match(contactPage);
						if (m2.Success)
						{
							IBizObjList articles = app.bizObjMgr.createList("Article");
							MatchCollection mc = conRe.Matches(m2.Value);
							if (mc.Count > 0)
							{
								foreach (Match m3 in mc)
								{
									string extId = m3.Groups["extId"].Value;
									string baseTitle = fixupTitle(m3.Groups["name"].Value);
									string parentExtId = m3.Groups["parentExtId"].Value;

									//TODO: This if check is specific to imdb, move to config somehow
									bool isTvShow = (baseTitle.StartsWith("\"") && baseTitle.EndsWith("\""));
									if (!isTvShow)
									{
										object[] pms = new object[] { scarfInfo, connector, extId, maxDepth, currentDepth + 1, contact };
										if(!(bool)scarfInfo.get("useParallel"))
											app.ruleMgr.execute("scarfContactConnector", pms);
										else if (connectorScarf == null)
											connectorScarf = app.ruleMgr.executeAsync("scarfContactConnector", null, null, pms);
										else
											connectorScarf.executeAsync("scarfContactConnector", null, null, pms);
									}
								}
							}
						}
						else
						{
							//TODO: Found no section for the connection list on page
						}

					}

					connectorScarf.waitForCompletion(true);
					if (setStart)
					{
						DateTime endTime = DateTime.Now;
						scarfInfo.put("runtime.endTime", endTime);
					}

				}
		}


		protected string fixupTitle(string title)
		{
			return title.Replace("&#x22;", "\"").Replace("&#x27;", "'").Replace("&#x26;", "&");
		}

		public string getDataPoint(IApplication app, IRuleContext context, IBizObj scarfInfo, IBizObj dataPoint, string extId)
		{
			string dp = "";

			string url = (string)dataPoint.get("detailUrl");
			url = url.Replace("$extId$", extId);
			string detailsPage = getPageText(url, scarfInfo);

			Regex detRe = new Regex((string)dataPoint.get("elementRegex"), RegexOptions.Singleline);
			Match m = detRe.Match(detailsPage);
			if (m.Success)
				dp = m.Groups[1].Value;
			return dp;
		}

		public void associateAllKnowContacts(IApplication app, IRuleContext context, IBizObj scarfInfo)
		{
			IBizObjList contacts = app.appData.get("DA.Contacts.GetContacts", context) as IBizObjList;
			foreach (IBizObj contact in contacts)
			{
				string extId = (string)contact.get("extId");
				if (!Cmn.IsEmpty(extId))
				{
					string url = (string)scarfInfo.get("contactDetailsPageUrl");
					url = url.Replace("$extId$", extId);
					string contactPage = getPageText(url, scarfInfo);

					IBizObjList connectors = scarfInfo.get("connectors") as IBizObjList;
					IRuleGroup connectorScarf = null;
					if (connectors != null)
						foreach (IBizObj connector in connectors)
						{
							Regex sectionRe = new Regex((string)connector.get("sectionRegex"), RegexOptions.Singleline);
							Regex conRe = new Regex((string)connector.get("elementRegex"), RegexOptions.Singleline);
							Match m2 = sectionRe.Match(contactPage);
							if (m2.Success)
							{
								IBizObjList articles = app.bizObjMgr.createList("Article");
								MatchCollection mc = conRe.Matches(m2.Value);
								if (mc.Count > 0)
								{
									foreach (Match m3 in mc)
									{
										string cextId = m3.Groups["extId"].Value;
										string baseTitle = fixupTitle(m3.Groups["name"].Value);
										string parentExtId = m3.Groups["parentExtId"].Value;

										string uniqueTitle = scarfInfo.get("id") + "_" + connector.get("id") + "_" + cextId;
										Swaf.BGuid.Guid objId = app.ruleMgr.execute("getArticleIdFromUniqueTitle", uniqueTitle, "Articles").CallResults as Swaf.BGuid.Guid;

										if (objId != null)
										{
											FlexiMap fm = new FlexiMap();
											fm.put("relatedItemId", contact.get("id"));
											fm.put("associationName", connector.get("id"));
											fm.put("articleId", objId);
											IBizObj assArt = app.appData.get("DA.Articles.CheckArticleAssociatedWithIdName", fm) as IBizObj;
											if (assArt == null)
											{
												IResults fmrc = app.ruleMgr.execute("createArticleAssociation", fm);
											}
										}

									}
								}
							}
							else
							{
								//TODO: Found no section for the connection list on page
							}

						}
				}
			}
		}

		public void scarfContactConnector(IApplication app, IRuleContext context, IBizObj scarfInfo, IBizObj connectorInfo, string extId, int maxDepth, int currentDepth, IBizObj contact)
		{
			string detailsPage = null;
			string uniqueTitle = scarfInfo.get("id") + "_" + connectorInfo.get("id") + "_" + extId;
			IMap uniqueConnectors = scarfInfo.get("runtime.uniqueConnectors") as IMap;

			Swaf.BGuid.Guid objId = null;
			if (uniqueConnectors.containsField(extId))
				return; // objId = uniqueConnectors.get(extId) as Swaf.BGuid.Guid;
			if(objId == null)
				objId = app.ruleMgr.execute("getArticleIdFromUniqueTitle", uniqueTitle, "Articles").CallResults as Swaf.BGuid.Guid;

			string url = (string)connectorInfo.get("elementDetailsUrl");
			url = url.Replace("$extId$", extId);
			detailsPage = getPageText(url, scarfInfo);

			if (objId == null)
			{
				Regex detRe = new Regex((string)connectorInfo.get("baseElementDetailRegex"), RegexOptions.Singleline);
				Match m = detRe.Match(detailsPage);
				if (m.Success)
				{
					IBizObj article = app.bizObjMgr.create("Article");
					string fextId = m.Groups["extId"].Value;
					string parentExtId = m.Groups["parentExtId"].Value;
					string baseTitle = fixupTitle(m.Groups["baseTitle"].Value);
					//string baseSummary = m.Groups["baseSummary"].Value;
					string imgId = m.Groups["imgId"].Value;

					IBizObjList dataPoints = connectorInfo.get("dataPoints") as IBizObjList;
					if (dataPoints != null)
						foreach (IBizObj dataPoint in dataPoints)
							if ("image".CompareTo(dataPoint.get("dataType")) == 0)
							{
								if (!Cmn.IsEmpty(imgId))
								{
									string origImg = ((string)dataPoint.get("detailUrl")).Replace("$imgId$", imgId);
									string ourImg = string.Format((string)dataPoint.get("imageNameFormat"), uniqueTitle);
									downloadImageIntoCache(app, scarfInfo, origImg, ourImg);
									article.put((string)dataPoint.get("fieldName"), ourImg);
								}
							}
							else
								article.put((string)dataPoint.get("fieldName"), getDataPoint(app, context, scarfInfo, dataPoint, extId));

					IBizObjList elSegs = connectorInfo.get("elementSegments") as IBizObjList;
					if (elSegs != null)
						foreach (IBizObj seg in elSegs)
						{
							Regex r = new Regex((string)seg.get("elementRegex"), RegexOptions.Singleline);
							MatchCollection rmc = r.Matches(detailsPage);
							if (rmc.Count > 0)
							{
								StringBuilder buff = new StringBuilder(150);
								foreach (Match rm in rmc)
									buff.Append(rm.Groups[1].Value).Append(", ");
								article.put((string)seg.get("fieldName"), buff.Length==0?"":buff.ToString(0, buff.Length-2));
							}
						}

					//if (dataPoints != null)
					//    foreach (IBizObj dataPoint in dataPoints)
					//        article.put((string)dataPoint.get("fieldName"), getDataPoint(app, context, scarfInfo, dataPoint, extId));

					objId = setupConArticleBizObj(context, article, scarfInfo, uniqueTitle, parentExtId, baseTitle, (string)article.get("summary"), (string)article.get("body"), true);
					//writeTrace(app, scarfInfo, "<ContactScarf id='{0}' event='foundMovie' extId='{1}' parentId='{2}' title='{3}'>\n<Summary>\n{4}\n</Summary>\n<Body>\n{5}\n</Body>\n</ContactScarf>",
					//    scarfInfo.get("id"), extId, parentExtId, baseTitle, baseSummary, article.get("body"));
					lock (scarfInfo)
					{
						uniqueConnectors.put(extId, objId);
					}
				}
				else
					writeTrace(app, scarfInfo, "<ContactScarf id='{0}' event='contactScarfPageSearchFailure' url='{1}' contactId='{2}' />",
						scarfInfo.get("id"), url, extId);
			}
			else
				uniqueConnectors.put(extId, objId);

			//Check for linkage between contact and content
			if (contact != null)
			{
				FlexiMap fm = new FlexiMap();
				fm.put("relatedItemId", contact.get("id"));
				fm.put("associationName", connectorInfo.get("id"));
				fm.put("articleId", objId);
				IBizObj assArt = app.appData.get("DA.Articles.CheckArticleAssociatedWithIdName", fm) as IBizObj;
				if (assArt == null)
				{
					IResults fmrc = app.ruleMgr.execute("createArticleAssociation", fm);
				}
			}

			if (detailsPage != null && currentDepth < maxDepth)
			{
				IBizObjList connectors = connectorInfo.get("connectors") as IBizObjList;
				if (connectors != null)
					foreach (IBizObj connector in connectors)
					{
						bool isContact = true;
						IRuleGroup connectorScarf = null;
						Regex sectionRe = new Regex((string)connector.get("sectionRegex"), RegexOptions.Singleline);
						Regex conRe = new Regex((string)connector.get("elementRegex"), RegexOptions.Singleline);
						Match m2 = sectionRe.Match(detailsPage);
						if (m2.Success)
						{
							IBizObjList articles = app.bizObjMgr.createList("Article");
							MatchCollection mc = conRe.Matches(m2.Value);
							if (mc.Count > 0)
							{
								int max = (int)connector.get("maxElements");
								if (max <= 0)
									max = mc.Count;
								for (int p = 0; p < max; p++)
								{
									Match m3 = mc[p];
									string cextId = m3.Groups["extId"].Value;
									if (isContact)
									{
										object[] pms = new object[] { scarfInfo, cextId, maxDepth, currentDepth + 1 };
										if (!(bool)scarfInfo.get("useParallel"))
											app.ruleMgr.execute("spiderScarfContact", pms);
										else if (connectorScarf == null)
											connectorScarf = app.ruleMgr.executeAsync("spiderScarfContact", null, null, pms);
										else
											connectorScarf.executeAsync("spiderScarfContact", null, null, pms);
									}

								}
							}
						}
						else
						{
							//TODO: Found no section for the connection list on page
						}

					}
			}				
		}

		protected Swaf.BGuid.Guid setupConArticleBizObj(IRuleContext context, IBizObj obj, IBizObj scarfInfo, string uniqueTitle, object parentId,
			string title, string summary, string body, bool forceUpdate)
		{
			IApplication app = Application.currentApp;
			string realPtStr = string.Format("{0}_{1}", scarfInfo.get("id"), parentId);
			//Look for parent
			Swaf.BGuid.Guid realParentId = app.ruleMgr.execute("getArticleIdFromUniqueTitle", realPtStr, "Articles").CallResults as Swaf.BGuid.Guid;
			if (realParentId == null)
			{
				Swaf.BGuid.Guid rootId = (Swaf.BGuid.Guid)scarfInfo.get("rootArticleId");
				if (!scarfInfo.isDirty("rootArticleId"))
					lock(scarfInfo)
						if (!scarfInfo.isDirty("rootArticleId"))
						{
							rootId = app.ruleMgr.execute("getArticleIdFromUniqueTitle", 
								scarfInfo.get("id"), "Articles").CallResults as Swaf.BGuid.Guid;
							if (rootId == null)
								rootId = createArticle(context, null, scarfInfo.get("id"), null, (string)scarfInfo.get("id"),"","");
							scarfInfo.put("rootArticleId", rootId);
						}
				realParentId = createArticle(context, null, realPtStr, rootId, (string)parentId, "", "");
			}
			lock (scarfInfo)
			{
				scarfInfo.put("runtime.totalNewArticlesFound", ((int)scarfInfo.get("runtime.totalNewArticlesFound")) + 1);
			}
			return createArticle(context, obj, uniqueTitle, realParentId, title, summary, body);
		}
		protected Swaf.BGuid.Guid createArticle(IRuleContext context, IBizObj obj, object uniqueTitle, Swaf.BGuid.Guid parentId, string title, string summary, string body)
		{
			context.put("delayTranslations", true);
			if(obj == null)
				obj = Application.currentApp.bizObjMgr.create("Article");
			Swaf.BGuid.Guid objId = new Swaf.BGuid.Guid();
			obj.put("id", objId);
			obj.put("uniqueTitle", uniqueTitle);
			obj.put("treeName", "Articles");
			obj.put("parentId", parentId);
			obj.put("baseTitle", title);
			obj.put("baseSummary", summary);
			obj.put("baseBody", body);
			IResults rc = context.RuleGroup.execute(context, "updateArticle", obj);
			return objId;
		}
	}
}
